package com.jingge.autojob.util.progress;


import com.google.common.util.concurrent.AtomicDouble;
import com.jingge.autojob.util.thread.SyncHelper;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 进度管理器
 *
 * @Author Huang Yongxiang
 * @Date 2022/06/09 13:48
 */
public class ProgressManager {
    private final AtomicInteger totalCount;
    private final AtomicInteger finishedCount;
    private final AtomicInteger pendingCount;
    private final AtomicDouble finishedPercent;
    private boolean isUseThreshold;
    /**
     * 变化阈值，即当前进度距离上一次获取进度的变化值需要大于该阈值才能获取
     */
    private Double changeThresholdPercent;
    /**
     * 变化阈值的单位
     */
    private ThresholdUnit unit;
    /**
     * 上一次获取时的进度
     */
    private double lastGetProgress;

    public ProgressManager() {
        this.totalCount = new AtomicInteger(0);
        this.finishedCount = new AtomicInteger(0);
        pendingCount = new AtomicInteger(0);
        finishedPercent = new AtomicDouble(0);
        isUseThreshold = false;
    }

    public ProgressManager(int totalCount, int finishedCount) {
        if (totalCount <= 0 || totalCount < finishedCount) {
            throw new IllegalArgumentException("总数目或已完成数目数据非法");
        }
        this.totalCount = new AtomicInteger(totalCount);
        this.finishedCount = new AtomicInteger(finishedCount);
        pendingCount = new AtomicInteger(totalCount - finishedCount);
        finishedPercent = new AtomicDouble(0);
        isUseThreshold = false;
    }

    public ProgressManager(int totalCount, int finishedCount, double changeThresholdPercent, String thresholdUnit) {
        if (totalCount < 0 || totalCount < finishedCount) {
            throw new IllegalArgumentException("总数目或已完成数目数据非法");
        }
        if (changeThresholdPercent <= 0) {
            throw new IllegalArgumentException("改变阈值不得为非负数");
        }
        ThresholdUnit unit = ThresholdUnit.convert(thresholdUnit);
        if (unit == ThresholdUnit.COUNT && (changeThresholdPercent > totalCount || (changeThresholdPercent > 0.0 && changeThresholdPercent < 1.0))) {
            throw new IllegalArgumentException("阈值非法");
        }
        if (unit == ThresholdUnit.PERCENT && (changeThresholdPercent < 0 || changeThresholdPercent > 100)) {
            throw new IllegalArgumentException("百分比为单位时，阈值应该在[0,100]");
        }
        this.totalCount = new AtomicInteger(totalCount);
        this.finishedCount = new AtomicInteger(finishedCount);
        this.changeThresholdPercent = changeThresholdPercent;
        this.unit = unit;
        pendingCount = new AtomicInteger(totalCount - finishedCount);
        finishedPercent = new AtomicDouble(0);
        isUseThreshold = true;
        lastGetProgress = 0;
    }

    public ProgressManager setChangeThresholdPercent(Double changeThresholdPercent, String thresholdUnit) {
        ThresholdUnit unit = ThresholdUnit.convert(thresholdUnit);
        this.changeThresholdPercent = changeThresholdPercent;
        this.unit = unit;
        this.isUseThreshold = true;
        return this;
    }


    public void setTotalCount(int totalCount) {
        this.totalCount.set(totalCount);
    }

    /**
     * 获取剩余待处理条目数
     *
     * @return int
     * @author Huang Yongxiang
     * @date 2022/6/9 15:20
     */
    public int getPendingCount() {
        pendingCount.set(totalCount.get() - finishedCount.get());
        return pendingCount.get();
    }

    /**
     * 获取调用方法时的进度
     *
     * @return double
     * @author Huang Yongxiang
     * @date 2022/6/9 15:20
     */
    public double getFinishedPercent() {
        if (finishedCount.get() == totalCount.get()) {
            return 100;
        }
        finishedPercent.set((finishedCount.get() * 100.0) / totalCount.get());
        return finishedPercent.get();
    }

    public int getTotalCount() {
        return totalCount.get();
    }

    public int getFinishedCount() {
        return finishedCount.get();
    }

    /**
     * 判断目前的进度相比上一次获取进度，期间变化是否达到阈值
     *
     * @return boolean
     * @author Huang Yongxiang
     * @date 2022/6/9 15:21
     */
    private boolean isAchieveThreshold() {
        return isAchieveThreshold(lastGetProgress);
    }

    public boolean isAchieveThreshold(double lastGetProgress) {
        if (!isUseThreshold) {
            throw new UnsupportedOperationException("不支持这种操作");
        }
        if (unit == ThresholdUnit.COUNT) {
            return finishedCount.get() - lastGetProgress > changeThresholdPercent;
        }
        return getFinishedPercent() - lastGetProgress > changeThresholdPercent;
    }

    /**
     * 同步获取调用方法时的进度，当阈值存在时则会同步等待直到进度变化达到阈值
     *
     * @return double
     * @author Huang Yongxiang
     * @date 2022/6/9 15:17
     */
    public double getFinishedPercentSync() {
        if (!isUseThreshold) {
            throw new UnsupportedOperationException("不支持同步获取操作，请直接使用getFinishedPercent()");
        }
        SyncHelper.aWaitQuietly(this::isAchieveThreshold);
        double finishedPercent = getFinishedPercent();
        lastGetProgress = unit == ThresholdUnit.COUNT ? finishedCount.get() : finishedPercent;
        return finishedPercent;
    }

    /**
     * 获取当前的进度情况，如果阈值单位是COUNt则返回当前已完成数目，如果为百分比则返回当前已完成百分比
     *
     * @return double
     * @author Huang Yongxiang
     * @date 2022/6/9 15:51
     */
    public double getNowGetProgress() {
        if (!isUseThreshold) {
            throw new UnsupportedOperationException("不支持这种操作");
        }
        return unit == ThresholdUnit.COUNT ? finishedCount.get() : finishedPercent.get();
    }

    /**
     * 更新当前已完成条目数
     *
     * @param finishedCount 更新后的已完成条目数
     * @return void
     * @author Huang Yongxiang
     * @date 2022/6/9 15:14
     */
    public void update(int finishedCount) {
        this.finishedCount.set(finishedCount);
        getPendingCount();
        getFinishedPercent();
    }

    /**
     * 原子性的使当前已处理条目数加1
     *
     * @return void
     * @author Huang Yongxiang
     * @date 2022/6/9 15:15
     */
    public void increment() {
        this.finishedCount.incrementAndGet();
        getPendingCount();
        getFinishedPercent();
    }

    public void add(int value) {
        this.finishedCount.addAndGet(value);
        getPendingCount();
        getFinishedPercent();
    }

    public boolean isOver() {
        return finishedCount.get() == totalCount.get();
    }

    public enum ThresholdUnit {
        PERCENT, COUNT;

        public static ThresholdUnit convert(String unit) {
            if ("COUNT".equalsIgnoreCase(unit)) {
                return COUNT;
            } else if ("PERCENT".equalsIgnoreCase(unit)) {
                return PERCENT;
            }
            return null;
        }
    }

    public static void main(String[] args) {
        int testNum = 20;
        long sleep = 1000;
        ProgressManager progressManager = new ProgressManager(testNum, 0, 1, "COUNT");
        Thread thread = new Thread(() -> {
            for (int i = 0; i < testNum; i++) {
                progressManager.increment();
                try {
                    Thread.sleep(sleep);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
        while (true) {
            System.out.printf("此时已完成数目为%d，当前进度%.4f%n", progressManager.finishedCount.get(), progressManager.getFinishedPercentSync());
        }


    }
}
